#ifndef MATERIALCUH
#define MATERIALCUH

struct hit_record;


#include "hitable.cuh"
#include"raytracingtool.cuh"
#include"texture.cuh"
class material  {
public:
    __device__ virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation,float& pdf, ray& scattered, curandState *local_rand_state) const = 0;
    __duel__ virtual ~material(){};
    __device__ virtual float scatter_pdf(const ray& InRay, const hit_record& info, const ray& scattered)const
    {
        return 1;
    }
    __device__ virtual vec3 emitted(float u, float v, const vec3& p) const 
    {
        return vec3(0,0,0);//默认发黑光即不发光
    }
};
//自发光材质
class diffuse_light : public material  {
    public:
        __duel__ diffuse_light(texture* a) : emit(a) {}
        __duel__ ~diffuse_light(){THISNULL(emit);}
        //禁止映射 即
        __device__ virtual bool scatter(
            const ray& r_in, const hit_record& rec, vec3& attenuation, float& pdf,ray& scattered, curandState *local_rand_state) const {
            return false;
        }
        
        __device__ virtual vec3 emitted(float u, float v, const vec3& p) const 
        {
            return emit->value(u, v, p);
        }

    public:
        texture* emit;
};


//漫反射
class lambertian : public material {
public:
    __duel__ lambertian(texture* a) : albedo(a) {}
    __device__ virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, float& pdf,ray& scattered, curandState *local_rand_state) const;
    __device__ virtual float scatter_pdf(const ray& InRay, const hit_record& info, const ray& scattered)const;
    __duel__ virtual ~lambertian()
    {
        THISNULL(albedo);
    };
    texture*albedo;
        
};
//
__device__  float lambertian::scatter_pdf(const ray& InRay, const hit_record& info, const ray& scattered)const
{
    float cosine=dot(info.normal,unit_vector(scattered.direction()));
    //cosine=ffmax(0,cosine);
    if(cosine<0.0)
    {
        cosine=0;
    }
    //return 1;
    return cosine/M_PI;
    
    
}
__device__ bool lambertian::scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, float& pdf,ray& scattered, curandState *local_rand_state) const
{
    onb uvw;
    uvw.build_from_w(rec.normal);
    
    //vec3 target = rec.p + rec.normal + random_in_unit_sphere(local_rand_state);
    do {
        vec3 direction = uvw.local(random_cosine_direction(local_rand_state));
        scattered = ray(rec.p, unit_vector(direction),r_in.time());
        pdf=dot(uvw.w(), scattered.direction()) / M_PI;
    } while (pdf == 0.0);
    
    attenuation = albedo->value(rec.u,rec.v,rec.p);
    
    return true;
}
class metal : public material {
public:
    __device__ metal(const vec3& a, float f) : albedo(a) { if (f < 1) fuzz = f; else fuzz = 1; }
    __device__ virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, float& pdf,ray& scattered, curandState *local_rand_state) const  {
        vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal);
        scattered = ray(rec.p, reflected + fuzz*random_in_unit_sphere(local_rand_state));
        attenuation = albedo;
        return (dot(scattered.direction(), rec.normal) > 0.0f);
    }
    vec3 albedo;
    float fuzz;
};

class dielectric : public material {
public:
    __device__ dielectric(float ri) : ref_idx(ri) {}
    __device__ virtual bool scatter(const ray& r_in,
                         const hit_record& rec,
                         vec3& attenuation,
                         float& pdf,
                         ray& scattered,
                         curandState *local_rand_state) const  {
        
        
        float ni_over_nt;
        attenuation = vec3(1.0, 1.0, 1.0);
        
        
        ni_over_nt = (rec.front_face) ? (1.0 / ref_idx) : (ref_idx);

        vec3 unit_direction = unit_vector(r_in.direction());
        float cos_theta = ffmin(dot(-unit_direction, rec.normal), 1.0);
        float sin_theta = sqrt(1.0 - cos_theta*cos_theta);
        if(sin_theta*ni_over_nt>1.0f)
        {
            vec3 reflected = reflect(r_in.direction(), rec.normal);
            scattered = ray(rec.p, reflected);
            return true;
        }
        float reflect_prob=schlick(cos_theta,ni_over_nt);
        if(curand_uniform(local_rand_state)<reflect_prob)
        {
            vec3 reflected = reflect(unit_direction, rec.normal);
            scattered = ray(rec.p, reflected);
            return true;
        }
        vec3 refracted= refract(unit_direction, rec.normal, ni_over_nt);
        
        scattered = ray(rec.p, refracted);
        return true;
        
    }

    float ref_idx;
};
#endif
